#include "gui.h"
#include "util.h"
#include <cerrno>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <assert.h>
#include <glade/glade.h>
#include <glib/gi18n.h>
using namespace std;

// Widgets.
GtkWidget*     mainWindow;
GtkWidget*     fileSaveDlg;

void run_file_choose_dlg(string& result) {
    if (gtk_dialog_run (GTK_DIALOG (fileSaveDlg))
            == GTK_RESPONSE_ACCEPT) {
        result =
            gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fileSaveDlg));
    }

    gtk_widget_hide(fileSaveDlg);
}

// Tracing widget.
GtkWidget*     procNameEntry;
GtkWidget*     procIdEntry;
GtkWidget*     findPidBtn;
GtkWidget*     traceScriptChooseBtn;
GtkWidget*     traceStartBtn;

GtkWidget*     traceSaveBtn;
GtkWidget*     traceSaveAsBtn;
GtkWidget*     traceDemangleBtn;
GtkWidget*     traceAnalyzeBtn;

GtkWidget*     traceOutTxtView;
GtkTextBuffer* traceOutBuffer;

static string scriptSavePath;

// Tracing Callbacks.
static void find_and_fill_pid() {
    vector<int> pid;
    const gchar* str = gtk_entry_get_text(GTK_ENTRY(procNameEntry));
    if (str && str[0] != '\0')
	pid = find_process_id(str);

    int size = pid.size();
    if (size == 0)
	return;
    else {
	ostringstream str;
	str << pid[0];
	for (int i = 1; i < size; ++i)
	    str << ", " << pid[i];
	gtk_entry_set_text(GTK_ENTRY(procIdEntry), str.str().c_str());
    }
}

void report_error(const char* msg) {
    GtkWidget* dialog = gtk_message_dialog_new (
            GTK_WINDOW(mainWindow),
            GTK_DIALOG_DESTROY_WITH_PARENT,
            GTK_MESSAGE_ERROR,
            GTK_BUTTONS_CLOSE,
            msg);
    gtk_dialog_run(GTK_DIALOG (dialog));
    gtk_widget_destroy(dialog);
}

// Whether the trace output buffer is empty.
static bool trace_buffer_empty(GtkTextBuffer* buf) {
    if (gtk_text_buffer_get_char_count(traceOutBuffer) == 0) {
        report_error(_("Trace output is empgy."));
        return true;
    } else
        return false;
}

// The porcess id of dtrace.
static pid_t dtracepid;

#define TRACE_BUFSIZE 512

void start_trace() {
    cout << "Start tracing" << endl;

    int pipefd[2];
    if (pipe(pipefd) < 0) {
        report_error(_("Pipe error"));
        return;
    }

    if ((dtracepid = fork()) < 0) {
        report_error(_("Fork error"));
        return;
    } else if (dtracepid == 0) { // Child process. Run dtrace.
        Close(pipefd[0]);
        dup2(pipefd[1], 1);
        if (execl("./sleepy", "1", NULL) < 0)
            _exit(127);
    } else {
        Close(pipefd[1]);
        static char buffer[TRACE_BUFSIZE];
        int count;
        GtkTextIter iter;
        gtk_text_buffer_get_start_iter(traceOutBuffer, &iter);
        while ((count = read(pipefd[0], buffer, TRACE_BUFSIZE)) > 0) {
            gtk_text_buffer_insert(traceOutBuffer, &iter,
                    buffer, count);
        }
        if (count < 0)
            report_error(_("Read dtrace output error"));
    }
}

void stop_trace() {

}

void save_text_buffer(GtkTextBuffer* buffer,
        const string& filename) {
    cout << "saving text buffer" << endl;
    GtkTextIter start, end;
    gtk_text_buffer_get_start_iter(traceOutBuffer, &start);
    gtk_text_buffer_get_end_iter(traceOutBuffer, &end);
    ofstream out(filename.c_str());

    if (out)
        out << gtk_text_buffer_get_text(traceOutBuffer,
                &start, &end, TRUE)
            << endl;
    else
        cerr << "Can't save trace output" << endl;
    out.close();
}

void saveas_trace_output() {
    if (trace_buffer_empty(traceOutBuffer))
        return;
    if (gtk_dialog_run (GTK_DIALOG (fileSaveDlg))
            == GTK_RESPONSE_ACCEPT) {
        scriptSavePath =
            gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fileSaveDlg));
        save_text_buffer(traceOutBuffer, scriptSavePath);
    }
    gtk_widget_hide(fileSaveDlg);
}

void save_trace_output() {
    if (trace_buffer_empty(traceOutBuffer))
        return;
    if (scriptSavePath.length() == 0) {
        if (gtk_dialog_run (GTK_DIALOG (fileSaveDlg))
                == GTK_RESPONSE_ACCEPT) {
            scriptSavePath =
                gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fileSaveDlg));
            save_text_buffer(traceOutBuffer, scriptSavePath);
        }
        gtk_widget_hide(fileSaveDlg);
    } else
        save_text_buffer(traceOutBuffer, scriptSavePath);
}

// Demangle C++ function name;
void demangle() {
    cout << "demangle" << endl;
}

// Initialization
void init_widget(GladeXML* xml) {
    mainWindow = glade_xml_get_widget(xml, "mainWindow");

    // Tracing.
    procNameEntry   = glade_xml_get_widget(xml, "procNameEntry");
    procIdEntry     = glade_xml_get_widget(xml, "procIdEntry");
    findPidBtn      = glade_xml_get_widget(xml, "findPidBtn");
    traceScriptChooseBtn = glade_xml_get_widget(xml, "traceScriptChooseBtn");
    assert(traceScriptChooseBtn != NULL);

    traceStartBtn    = glade_xml_get_widget(xml, "traceStartBtn");
    assert(traceStartBtn != NULL);

    traceSaveBtn     = glade_xml_get_widget(xml, "traceSaveBtn");
    assert(traceSaveBtn != NULL);

    traceSaveAsBtn     = glade_xml_get_widget(xml, "traceSaveAsBtn");
    assert(traceSaveAsBtn != NULL);

    traceDemangleBtn = glade_xml_get_widget(xml, "traceDemangleBtn");
    assert(traceDemangleBtn != NULL);

    traceAnalyzeBtn     = glade_xml_get_widget(xml, "traceAnalyzeBtn");
    
    traceOutTxtView  = glade_xml_get_widget(xml, "traceOutTxtView");
    traceOutBuffer   = gtk_text_view_get_buffer(GTK_TEXT_VIEW(traceOutTxtView));
    
    fileSaveDlg = gtk_file_chooser_dialog_new(_("Save File"),
            GTK_WINDOW(mainWindow),
            GTK_FILE_CHOOSER_ACTION_OPEN,
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
            GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
            NULL);
}

void init_signal() {
    g_signal_connect(G_OBJECT(findPidBtn), "clicked",
            G_CALLBACK(find_and_fill_pid), NULL);
    g_signal_connect(G_OBJECT(traceSaveBtn), "clicked",
            G_CALLBACK(save_trace_output), NULL);
    g_signal_connect(G_OBJECT(traceSaveAsBtn), "clicked",
            G_CALLBACK(saveas_trace_output), NULL);
    g_signal_connect(G_OBJECT(traceStartBtn), "clicked",
            G_CALLBACK(start_trace), NULL);
    g_signal_connect(G_OBJECT(traceDemangleBtn), "clicked",
            G_CALLBACK(demangle), NULL);
}

void init_gui() {
    GladeXML* xml;

    xml = glade_xml_new("ui.glade", NULL, NULL);
    init_widget(xml);

    /* connect signal handlers */
    glade_xml_signal_autoconnect(xml);
    init_signal();
}

